package middlewares

import (
	"context"
	"errors"
	"github.com/gin-gonic/gin"
	"log"
	"main/src/apps"
	"main/src/configs"
	"main/src/utils"
	"net/http"
	"sort"
	"strings"
	"time"
)

type allow struct {
	paths []string
}

func (a *allow) sort() {
	sort.Strings(a.paths)
}

func (a *allow) in(target string) bool {
	index := sort.SearchStrings(a.paths, target)
	if index < len(a.paths) && a.paths[index] == target {
		return true
	}
	return false
}

var allowPath = allow{
	paths: []string{
		"/user/login",

		// server app
		"/api/get_content",
		"/api/get_all_domain",
	},
}

var loginAllowPath = allow{
	paths: []string{
		"/log/get",
		"/user/get",
		"/skip/get",
		"/site/get",
		"/content/get",
		"/content/image/get",
		"/content/keyword/get",

		"/skip/delete",
		"/skip/update",
		"/skip/create",
		"/skip/export",

		"/content/keyword/create",
		"/content/keyword/delete",
		"/content/keyword/update",
		"/content/keyword/export",

		"/content/image/create",
		"/content/image/delete",
		"/content/image/update",
		"/content/image/export",

		"/content/create",
		"/content/delete",
		"/content/update",
	},
}

var superUserAllowPath = allow{
	paths: []string{
		"/log/delete",
		"/log/update",
		"/log/create",

		"/site/delete",
		"/site/update",
		"/site/create",
		"/site/export",

		"/user/update/is_super",
		"/user/update/password",
		"/user/delete",
		"/user/create",
		"/user/update/back_up",
	},
}

// JWTAuth 中间件，检查token
func JWTAuth() gin.HandlerFunc {
	allowPath.sort()
	loginAllowPath.sort()
	superUserAllowPath.sort()

	return func(ctx *gin.Context) {
		log.Println(ctx.FullPath())
		if allowPath.in(ctx.FullPath()) {
			// 不需要权限的路由
			ctx.Next()
			return
		} else if !(loginAllowPath.in(ctx.FullPath())) && !(superUserAllowPath.in(ctx.FullPath())) {
			// 不需要权限的路由
			ctx.Next()
			return
		} else {
			authHeader := ctx.Request.Header.Get("Authorization")
			if authHeader == "" {
				ctx.JSON(http.StatusOK, gin.H{
					"code": -1,
					"msg":  "无权限访问，请求未携带token",
				})
				ctx.Abort() //结束后续操作
				return
			}
			//按空格拆分
			parts := strings.SplitN(authHeader, " ", 2)
			token := parts[1]
			if !(len(parts) == 2 && parts[0] == "Bearer") {
				ctx.JSON(http.StatusOK, gin.H{
					"code": -1,
					"msg":  "请求头中auth格式有误",
				})
				ctx.Abort()
				return
			}

			if isLogin(ctx, token) {
				var ctxUser, _ = ctx.Get("user")
				// 需要登录的路由
				if loginAllowPath.in(ctx.FullPath()) {
					ctx.Next()
					// 不需要超级用户的路由
				} else if superUserAllowPath.in(ctx.FullPath()) {
					// 需要超级用户的路由
					if isSuper(ctxUser.(utils.UserJWTInfo)) {
						ctx.Next()
					} else {
						utils.Response(ctx, "无权限！", utils.Status.Error, nil)
						ctx.Abort()
					}
				}
				return

			}
			// 未登录

		}
	}
}

// checkUserInfo 检查用户名信息
func checkUserInfo(claims *utils.CustomClaims, token string) error {
	username := claims.Username
	password := claims.Password
	jwtUser := getUserInfoFromRedis(token)
	//获取数据库用户名及密码
	if username == jwtUser.Username && jwtUser.Password == password {
		return nil
	}
	return errors.New("用户名或密码错误")

}

func getUserInfoFromRedis(token string) *utils.UserJWTInfo {
	jwtUser := utils.UserJWTInfo{}
	_ = apps.RedisClient.Get(context.Background(), token).Scan(&jwtUser)
	return &jwtUser
}

func isLogin(ctx *gin.Context, token string) (_isLogin bool) {
	redisUser := getUserInfoFromRedis(token)
	if redisUser == nil {
		// 检查redis是否存在用户会话
		utils.Response(ctx, "登录超时，或未登录", utils.Status.Error, nil)
		ctx.Abort()
		_isLogin = false
	} else {
		_isLogin = true
		// 检查token是否过期
		jwtUser, err := utils.ParseToken(token)
		if jwtUser == nil {
			// 以过期，刷新token,允许
			redisToken, jwtErr := utils.GenToken(redisUser)
			if jwtErr != nil {

			}
			err = apps.RedisClient.Set(context.Background(), redisToken, redisUser, time.Minute*time.Duration(configs.Config.TokenLiveTime)).Err()
			log.Println("token过期")
			// header加上token重置
			ctx.Header("ResetToken", "Bearer "+redisToken)
			if err != nil {
				ctx.Abort()
				utils.Response(ctx, "服务端错误:"+err.Error(), utils.Status.ServerError, nil)
				_isLogin = false
			}
		}
		// 没有过期， 允许
	}
	ctx.Set("user", *redisUser)
	return _isLogin
}

func isSuper(u utils.UserJWTInfo) bool {
	return u.IsSuper
}
